/***************************************************************************
 *   This is a part of the NextCAD Web APP.                                *
 *   Copyright (C) AMCAX, All rights reserved.                             *
 ***************************************************************************/

import { NonDialogPickableCommandForm } from '@app-cad/common/components/command-form/pickable';
import { useCommand } from '@app-cad/common/hooks/useCommand';
import { useTools } from '@app-cad/common/hooks/useTools';
import { RJSFSchema } from '@rjsf/utils';
import { omitBy } from 'lodash';
import { useEffect, useMemo } from 'react';
import ReverseIconWidget from '../../../../../common/RJSFWidgets/ReverseIconWidget';
import {
  angle2ForCountersunk,
  diameter2ForCountersunk,
  getMinimumCommonFields,
  getMinimumCommonParamsForCommand,
  hatDepthForCountersunk,
  initFormDataWithProps,
  sunkOption,
  minimumFormValidation,
} from './common-fields';
import { commonUiSchema } from './common-ui-schema';
import { ChildHoleProps, MinCommonFormData, MinCommonParams } from './types';

type FormData = {
  type?: string;
  sunkOption?: 'depthAngle' | 'depthDiameter' | 'angleDiameter';
  diameter2?: number;
  hatDepth?: number;
  angle2?: number;
} & MinCommonFormData;

type Params = {
  type?: string;
  sunkOption?: 'depthAngle' | 'depthDiameter' | 'angleDiameter';
  diameter2?: number;
  hatDepth?: number;
  angle2?: number;
} & MinCommonParams;

export function CountersunkHole({
  onClose,
  editDxid,
  props,
  formData,
  setFormData,
}: ChildHoleProps<Params, FormData>) {
  const schema: RJSFSchema = useMemo(() => {
    const properties = omitBy(
      {
        ...getMinimumCommonFields(formData),
        sunkOption,
        ...(formData.sunkOption === 'depthDiameter'
          ? {
              hatDepth: hatDepthForCountersunk,
              diameter2: diameter2ForCountersunk,
            }
          : {}),
        ...(formData.sunkOption === 'angleDiameter'
          ? {
              angle2: angle2ForCountersunk,
              diameter2: diameter2ForCountersunk,
            }
          : {}),
        ...(formData.sunkOption === 'depthAngle'
          ? {
              hatDepth: hatDepthForCountersunk,
              angle2: angle2ForCountersunk,
            }
          : {}),
      },
      (val) => val == null,
    );
    return {
      type: 'object',
      properties,
    } as RJSFSchema;
  }, [formData.bottomType, formData.mode, formData.sunkOption]);
  const { picker } = useTools();

  useEffect(() => {
    preview();
  }, [formData]);

  useEffect(() => {
    if (props == null) {
      setFormData({
        ...formData,
        mode: formData.mode || 'distance',
        sunkOption: formData.sunkOption || 'depthAngle',
      });
      return;
    }

    const { params } = props;
    const { sunkOption, diameter2, angle2, hatDepth } = params;

    initFormDataWithProps(params, initEditDependency, picker).then((minForm) =>
      setFormData({
        ...minForm,
        sunkOption: sunkOption || 'depthAngle',
        diameter2,
        angle2: angle2 || 90,
        hatDepth,
      }),
    );
  }, [editDxid]);

  const isFormDataValid = () => {
    const isHatDepthValid =
      formData.hatDepth !== null &&
      (formData.mode !== 'distance' || formData.hatDepth < formData.depth);
    const isDiameter2Valid =
      formData.diameter2 !== null && formData.diameter2 > formData.diameter;
    const isAngle2Valid =
      formData.angle2 !== null && formData.angle2 > 0 && formData.angle2 < 180;
    const isDepthValidForAngleDiameterOption =
      (formData.diameter2 - formData.diameter) /
        2 /
        Math.tan((formData.angle2 / 2) * (Math.PI / 180)) <
      formData.depth;

    return (
      minimumFormValidation(formData) &&
      ((formData.sunkOption === 'depthDiameter' &&
        isDiameter2Valid &&
        isHatDepthValid) ||
        (formData.sunkOption === 'angleDiameter' &&
          isDiameter2Valid &&
          isAngle2Valid &&
          isDepthValidForAngleDiameterOption) ||
        (formData.sunkOption === 'depthAngle' &&
          isHatDepthValid &&
          isAngle2Valid))
    );
  };

  const checkAndFinishDraw = async () => {
    if (!isFormDataValid()) {
      return;
    }

    await execute(false);
    onClose();
  };

  const { initEditDependency, initingEdit, clear, confirmCommand, isLoading } =
    useCommand('cmd-holes', {
      commandName: 'CreateHoles',
      editDxid,
      dependencies: props?.params ? [props.params.base] : [],
      onClose,
      finishDraw: checkAndFinishDraw,
    });

  const execute = (preview: boolean) => {
    return confirmCommand(
      {
        a: {
          type: 'Countersunk',
          ...getMinimumCommonParamsForCommand(formData),
          sunkOption: formData.sunkOption,
          diameter2: formData.diameter2,
          hatDepth: formData.hatDepth,
          angle2: formData.angle2,
        } as Params,
      },
      preview,
    );
  };

  const preview = async () => {
    if (!isFormDataValid()) {
      clear();
      return;
    }

    await execute(true);
  };

  const finalFormData = useMemo(
    () => ({
      ...formData,
    }),
    [formData],
  );

  return (
    <NonDialogPickableCommandForm
      disabled={initingEdit}
      onCancel={onClose}
      schema={schema}
      submitLoading={isLoading}
      formData={finalFormData}
      widgets={{
        reverseIcon: ReverseIconWidget,
      }}
      onChange={(v) => {
        setFormData(v);
      }}
      submitText='完成'
      onSubmit={checkAndFinishDraw}
      extraUiSchema={{
        ...commonUiSchema,
        diameter2: {
          'ui:placeholder': '埋头孔直径',
        },
        hatDepth: {
          'ui:placeholder': '埋头孔深度',
        },
        angle2: {
          'ui:placeholder': '埋头孔角度',
        },
      }}
      isFormDataValid={isFormDataValid()}
    />
  );
}
